//
// Created by martin on 2/15/22.
//

#include "InetAddress.h"
#include "muduo/base/Logging.h"
#include "muduo/net/Endian.h"
#include "muduo/net/SocketsOps.h"

#include <netdb.h>
#include <netinet/in.h>
#include <stddef.h>
#include <assert.h>

#pragma GCC diagnostic ignored "-Wold-style-cast"
static const in_addr_t kInaddrAny = INADDR_ANY;
static const in_addr_t kInaddrLoopback = INADDR_LOOPBACK;
#pragma GCC diagnostic error "-Wold-style-cast"

#if 0
/* Internet address.  */
typedef uint32_t in_addr_t;
struct in_addr
  {
    in_addr_t s_addr;
  };

/* Structure describing an Internet socket address.  */
struct sockaddr_in
{
    __SOCKADDR_COMMON (sin_);
    in_port_t sin_port;			/* Port number.  */
    struct in_addr sin_addr;		/* Internet address.  */

    /* Pad to fileSize of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
                           __SOCKADDR_COMMON_SIZE -
                           sizeof (in_port_t) -
                           sizeof (struct in_addr)];
};

/* Ditto, for IPv6.  */
struct sockaddr_in6
  {
    __SOCKADDR_COMMON (sin6_);
    in_port_t sin6_port;	/* Transport layer port # */
    uint32_t sin6_flowinfo;	/* IPv6 flow information */
    struct in6_addr sin6_addr;	/* IPv6 address */
    uint32_t sin6_scope_id;	/* IPv6 scope-id */
  };

#endif

using namespace muduo;
using namespace muduo::net;

static_assert(sizeof(InetAddress) == sizeof(struct sockaddr_in6),
        "InetAddress is same fileSize as sockaddr_in16");
static_assert(offsetof(sockaddr_in, sin_family) == 0, "sin_family offset 0");
static_assert(offsetof(sockaddr_in6, sin6_family) == 0, "sin6_family offset 0");
static_assert(offsetof(sockaddr_in, sin_port) == 2, "sin_port offset 2");
static_assert(offsetof(sockaddr_in6, sin6_port) == 2, "sin6_port offset 2");

/**
 * 构造InetAddress对象
 * @param portArg 端口号
 * @param loopbackOnly　决定是否为回环地址
 * @param ipv6　决定是否为ipv6地址
 * @note 注意addr_/addr6_中存放的是网络字节序
 */
InetAddress::InetAddress(uint16_t portArg, bool loopbackOnly, bool ipv6)
{
    // 确保addr6_/addr_ 在InetAddress class内存中的偏移
    static_assert(offsetof(InetAddress, addr6_) == 0, "addr6_ offset 0");
    static_assert(offsetof(InetAddress, addr_) == 0, "addr_ offset 0");
    if (ipv6)
    { // ipv6地址
        memZero(&addr6_, sizeof(addr6_));
        addr6_.sin6_family = AF_INET6;
        // in6addr_loopback: 回环地址; in6addr_any： 任意地址
        in6_addr ip = loopbackOnly ? in6addr_loopback : in6addr_any;
        addr6_.sin6_addr = ip; // ip地址
        addr6_.sin6_port = sockets::hostToNetwork16(portArg); // 端口号
    }
    else
    { // ipv4地址
        memZero(&addr_, sizeof(addr_));
        addr_.sin_family = AF_INET;
        // kInaddrLoopback: 回环地址; kInaddrAny： 任意地址
        in_addr_t ip = loopbackOnly ? kInaddrLoopback : kInaddrAny;
        addr_.sin_addr.s_addr = sockets::hostToNetwork32(ip); // ip地址
        addr_.sin_port = sockets::hostToNetwork16(portArg);   // 端口号
    }
}

/*
 * 根据ip地址字符串形式 + 端口号, 构造InetAddress对象
 * IPv6: 2409:8a4c:662f:2900:9b2:63:9618:56c0
 * IPv4: 127.0.0.1
 */
InetAddress::InetAddress(StringArg ip, uint16_t portArg, bool ipv6)
{
    if (ipv6 || strchr(ip.c_str(), ':'))
    { // 指定为ipv6地址类型, 或ip地址字符串中包含':'
        memZero(&addr6_, sizeof(addr6_));
        sockets::fromIpPort(ip.c_str(), portArg, &addr6_); // 将冒号16进制表示的ip地址+port, 转化为addr6_结构
    }
    else
    {
        memZero(&addr_, sizeof(addr_));
        sockets::fromIpPort(ip.c_str(), portArg, &addr_); // 将冒号16进制表示的ip地址+port, 转化为addr6_结构
    }
}

/**
 * 当对象ip地址转换为字符串, 如果是IPv4, 就转换为点分十进制; 如果是IPv6, 就转换为冒号十六进制.
 */
string InetAddress::toIp() const
{
    char buf[64] = "";
    sockets::toIp(buf, sizeof(buf), getSockAddr());
    return buf;
}

/**
 * 当对象ip地址＆port信息 转换为字符串, 如果是IPv4, 就转换为点分十进制; 如果是IPv6, 就转换为冒号十六进制.
 */
string InetAddress::toIpPort() const
{
    char buf[64] = "";
    sockets::toIpPort(buf, sizeof(buf), getSockAddr());
    return buf;
}

uint16_t InetAddress::port() const
{
    return sockets::networkToHost16(portNetEndian());
}

uint32_t InetAddress::ipv4NetEndian() const
{
    assert(family() == AF_INET);
    return addr_.sin_addr.s_addr;
}

/* 转换用的临时缓存. 因为占用空间比较大, 不用函数栈; 又要确保线程安全, 有可能经常调用, 因此用thread local 变量 */
static __thread char t_resolveBuffer[64 * 1024]; // 64KB = 64*1024
/**
 * hostname转换为InetAddress
 * @param hostname 主机名, 可以是本地主机名, 如"localhost"; 也可以是远程域名, 如"google.com", "192.168.0.10",
 * @param out[out] 指向一个InetAddress对象, 存放地址信息
 * @return 转换结果. true: 表示转换成功; false: 表示转换失败.
 */
bool InetAddress::resolve(StringArg hostname, InetAddress *out)
{
    assert(out != NULL);
    struct hostent hent;
    struct hostent* he = NULL;
    int herrno = 0;
    memZero(&hent, sizeof(hent));

    /* 将主机名或IPv4地址(点分十进制)转换为hostent结构 */
    int ret = gethostbyname_r(hostname.c_str(), &hent, t_resolveBuffer, sizeof(t_resolveBuffer),
                              &he, &herrno);
    if (ret == 0 && he != NULL)
    {
        assert(he->h_addrtype == AF_INET && he->h_length == sizeof(uint32_t)); // AF_INET: ipv4
        out->addr_.sin_addr = *reinterpret_cast<struct in_addr*>(he->h_addr);
        return true;
    }
    else
    { // error, gai_strerror(3) 能获取错误字符串信息
        if (ret)
        {
            LOG_SYSERR << "InetAddress::resolve";
        }
    }
    return false;
}

void InetAddress::setScopedId(uint32_t scope_id)
{
    if (family() == AF_INET6)
    {
        addr6_.sin6_scope_id = scope_id;
    }
}
